#include <typeinfo>
#include "../include/AnimationManager.h"
#include "../include/BoneManager.h"
#include "../include/ProjectMain.h"

const long AnimationManager::ID_TIMER = wxNewId();

BEGIN_EVENT_TABLE(AnimationManager,wxEvtHandler)
    EVT_TIMER(ID_TIMER, AnimationManager::OnTimer)
END_EVENT_TABLE()

AnimationManager::AnimationManager(BoneManager* boneManager, Config* config)
{
    this->boneManager   = boneManager;
    this->config        = config;
    this->timer         = new wxTimer(this, ID_TIMER);
    this->frameNo       = 0;
}

AnimationManager::~AnimationManager()
{
    std::map <unsigned long, std::vector< BoneState*>* >::iterator mapIt;
    std::vector<BoneState*>::iterator vecIt;

    for (mapIt = framesInfo.begin(); mapIt != framesInfo.end(); ++mapIt)
    {
        for(vecIt = (*mapIt).second->begin(); vecIt != (*mapIt).second->end(); vecIt++)
            delete *vecIt;
        delete (*mapIt).second; // !!!!!!!!!!!!!!! seg on progem exit
    }
}

const std::map< unsigned long, std::vector< BoneState*>* >& AnimationManager::getFramesInfo()
{
    return framesInfo;
}

void AnimationManager::deleteState(unsigned long frameNumber)
{
    //check if base state
    if (frameNumber == 0)
    {
        wxMessageBox(_("You can't remove this skeleton state"));
        return;
    }

    // clear state with specified number, if exist
    std::map <unsigned long, std::vector< BoneState*>* >::iterator mapIt;
    std::vector<BoneState*>::iterator vecIt;

    mapIt = framesInfo.find ( frameNumber );
    if ( mapIt != framesInfo.end() )
    {
        std::vector<BoneState*>* tmp = (*mapIt).second;

        for(vecIt = (*mapIt).second->begin(); vecIt != (*mapIt).second->end(); vecIt++)
            delete *vecIt;

        delete (*mapIt).second;
        framesInfo.erase(frameNumber);

        if (tmp == boneManager->getBonesStates())
        {
            boneManager->saveSkeletonState(true); // save states on current vector,
            boneManager->applySkeletonState(frameNumber);
        }
    }

    if( getFramesInfo().size()<=1 )
        boneManager->getProjectFrame()->enableAddBoneMode( true );
}

void AnimationManager::addState(unsigned long frameNumber)
{
    addState (frameNumber, boneManager->saveSkeletonState(true));
                           // save states in new vector
}

void AnimationManager::showFrame(unsigned long frameNumber)
{
    boneManager->saveSkeletonState(false); // save states on current vector,
    boneManager->applySkeletonState(frameNumber);
}

void AnimationManager::addState(unsigned long frameNumber, std::vector<BoneState*>* states)
{
    try
    {
         wxString test;
         test << _("adding number ") << frameNumber << _("\n");
         std::vector<BoneState*>::iterator vecIt;
         for(vecIt= states->begin(); vecIt != (*states).end(); vecIt++)
             test << _("id = ") << (*vecIt)->boneId << _("\n");
         //wxMessageBox(test);

        // remove last state if exist
        std::map <unsigned long, std::vector< BoneState*>* >::iterator mapIt;
        mapIt = framesInfo.find ( frameNumber );

        if ( mapIt != framesInfo.end() )
        {
            //wxMessageBox(_("deleting previous"));
            for(vecIt = (*mapIt).second->begin(); vecIt != (*mapIt).second->end(); vecIt++)
                delete *vecIt;
            framesInfo.erase(frameNumber);
        } // end romoving

        // adding state
        framesInfo.insert( std::pair<unsigned long, std::vector<BoneState*>* >(frameNumber, states) );

        if( getFramesInfo().size()>1 )
            boneManager->getProjectFrame()->enableAddBoneMode( false );

    }
    catch (std::bad_cast ex)
    {
        wxMessageBox( wxT("Dynamic cast fail in method AnimatioManager::addState(unsigned long, std::vector<BoneState*>)") );
        std::cerr << ex.what() << std::endl;
        return;
    }
}

bool AnimationManager::hasState(unsigned long frameNumber)
{
    std::map <unsigned long, std::vector< BoneState*>* >::iterator mapIt;
    mapIt = framesInfo.find(frameNumber);

    if ( mapIt == framesInfo.end() )
        return false;
    else
        return true;
}


std::vector<BoneState*>* AnimationManager::calculateFrameState(unsigned long frameNumber)
{
    // There is no frames in map
    if(framesInfo.empty())
    {
        std::cout << "Zbior pustych klatek!/n";
        wxMessageBox(_("There are no saved frames !!! WRONG !!!"));
        return NULL;
    }

    // searching if specified frame number has already calculated Sceleton state
    std::map <unsigned long, std::vector< BoneState*>* >::iterator mapIt;
    mapIt = framesInfo.find(frameNumber);

    // frame found
    if ( mapIt != framesInfo.end() )
    {
        wxString tt;
        tt << _("state found") << frameNumber;
        //wxMessageBox(tt);
        return framesInfo[frameNumber];
    }//STATE FOUND


    // searched frame number is further than last number in map
    if ( frameNumber > (*framesInfo.rbegin()).first )
    {
        //returns last state
        wxString tt;
        tt << frameNumber << _(" > than last in map return last = ") << (*framesInfo.rbegin()).first;
        //wxMessageBox(tt);

        RootState *rootState = new RootState ( *(dynamic_cast<RootState*> ((*((*(framesInfo.rbegin())).second))[0])) );

        std::vector<BoneState*>* stateVector = new std::vector<BoneState*>();
        stateVector->push_back(rootState);

        BoneState *state;
        // for all other bones compute the angle
        for(unsigned i = 1; i < (*(framesInfo.rbegin())).second->size(); ++i)
        {
            state = new BoneState ( *((*((*(framesInfo.rbegin())).second))[i]) );
            stateVector->push_back(state);
        }
        return stateVector;
    }// STATE FURTHER THAN LAST IN MAP


    // searching previous and next of frameNumebr, elements with saved states
    std::map <unsigned long, std::vector< BoneState*>* >::iterator it_prev, it_next;

    it_prev = framesInfo.begin();
    it_next = framesInfo.begin();
    for(mapIt = framesInfo.begin(); mapIt != framesInfo.end(); ++mapIt)
    {
        if( (*mapIt).first < frameNumber )
            it_prev = mapIt;
        else
        {
            it_next = mapIt;
            break;
        }
    }

wxString test;
test << _("calculating ") << frameNumber << _(" between previous= ") << (*it_prev).first << _(" next= ") << (*it_next).first;
//wxMessageBox(test);

    // calculating state on specified frame number
    try
    {
        unsigned int differencePrevNext = (*it_next).first - (*it_prev).first;
        unsigned int differenceCurrentPrev = frameNumber  - (*it_prev).first;

        //for root bone (all sceleton) set the translation vector
        RootState *rootState = new RootState ( *(dynamic_cast<RootState*> ((*((*it_prev).second))[0])) );
        RootState *nextRootState = dynamic_cast<RootState*> ((*((*it_next).second))[0]);

        /*wxString test;
        test << _("root loop in anim = ") << rootState->loopNumber;
        test << _("loop difference = ") << nextRootState->loopNumber - rootState->loopNumber;
*/
        // if root has animation set on
        if (rootState->motion)
        {
            rootState->translation.x += (double)(nextRootState->translation.x - rootState->translation.x) / (double)differencePrevNext * differenceCurrentPrev;
            rootState->translation.y += (double)(nextRootState->translation.y - rootState->translation.y) / (double)differencePrevNext * differenceCurrentPrev;
            rootState->loops += (double)(nextRootState->loops - rootState->loops) / (double)differencePrevNext * differenceCurrentPrev;
        }
//        int loopDif = (nextRootState->loopNumber - rootState->loopNumber);
//        double deltaAngleFromLoop =  (2*M_PI* (loopDif / (double)differencePrevNext)) * differenceCurrentPrev;
//        bool isEven = (abs(loopDif) % (int)differencePrevNext) == 0 ? 0 : 1;//isEven = ( abs(loopDif) / (int)differencePrevNext ) % 2;
//        if (abs(loopDif) == 1)
//            isEven = 1;

        //rootState->loopNumber +=  loopDif/ (int)differencePrevNext * (int)differenceCurrentPrev;
        //rootState->deltaAngle += isEven*deltaAngleFromLoop + (double)(nextRootState->deltaAngle - rootState->deltaAngle) / (double)differencePrevNext * differenceCurrentPrev;

        //wxString test2;
        //test2 << _("rootDangle = ") << deltaAngleFromLoop;
        //wxMessageBox(test2);

//        if(!isEven){
//             ;//wxMessageBox(_("Root even"));
//        }


        std::vector<BoneState*>* stateVector = new std::vector<BoneState*>();
        stateVector->push_back(rootState);

        BoneState *state;
        // for all other bones compute the angle
        for(unsigned i = 1; i < (*it_prev).second->size(); ++i)
        {
            state = new BoneState ( *((*((*it_prev).second))[i]) );

//            loopDif = (*it_next).second[i]->loopNumber - (*it_prev).second[i]->loopNumber;
//            deltaAngleFromLoop = (2*M_PI* loopDif / (double) differencePrevNext) *differenceCurrentPrev;
//            //isEven =  ( abs(loopDif) / (int)differencePrevNext ) % 2;
//            isEven = (abs(loopDif) % (int)differencePrevNext) == 0 ? 0 : 1;
//            //if ()
//
//            if (abs(loopDif) == 1) // if there was only one loop, delta from loop must be added
//                isEven = 1;

            // if bone has animation set on
            if (state->motion)
                state->loops += (double)( (*((*it_next).second))[i]->loops - (*((*it_prev).second))[i]->loops ) / (double)differencePrevNext * differenceCurrentPrev;
            //state->loopNumber += (loopDif) / (int)differencePrevNext * (int)differenceCurrentPrev;
            //state->deltaAngle += isEven*deltaAngleFromLoop + (double)( (*it_next).second[i]->deltaAngle - (*it_prev).second[i]->deltaAngle ) / (double)differencePrevNext * differenceCurrentPrev;

            /*if(state->deltaAngle > 2*M_PI){
                wxMessageBox(_("wiekszy"));
            }*/

            stateVector->push_back(state);
        }

            wxString test;
            //test << _("roop new angle = ") << rootState->deltaAngle << _("\n");
            //test << _("fchild new angle = ") << stateVector[1]->deltaAngle << _("\n");
//            for(unsigned i = 0; i < (*it_prev).second.size(); ++i)
//            {
//                //test << _("calculatedD") << stateVector[i]->deltaAngle << _("id = ") << (*it_prev).second[i]->boneId << _(" loops_prev = ") << (*it_prev).second[i]->loops <<  _(" loops_next = ") << (*it_next).second[i]->loops << _("\n");
//                     //<< _(" delata_prev = ") << (*it_prev).second[i]->deltaAngle <<  _(" delata_next = ") << (*it_next).second[i]->deltaAngle << _("\n");
//            }
            //wxMessageBox(test);

        return stateVector;
    }
    catch (std::bad_cast ex)
    {
        wxMessageBox( wxT("Dynamic cast fail in method AnimatioManager::calculateFrameState(unsigned long)") );
        std::cerr << ex.what() << std::endl;
        return (*framesInfo.begin()).second;
    }

}

void AnimationManager::start()
{
    timer->Start( 1000/config->getFPS() );
}

void AnimationManager::play()
{
    showFrame( frameNo );
    frameNo++;

//    while( running )
//    {
////        if((deltaT=fps.getTime())<(1000.0/(double)FPS))
////            deltaT=(1000.0/(double)FPS);
////        if(fps.getTime()<1000.0/FPS)
////            SDL_Delay(1000.0/(double)FPS-fps.getTime());
//    }
}

void AnimationManager::pause()
{
    timer->Stop();
}

void AnimationManager::stop()
{
    timer->Stop();
    showFrame( 0 );
    frameNo = 0;
}

void AnimationManager::OnTimer(wxTimerEvent& event)
{
    play();
}
